package ms.unicorn.modellanguage;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import ms.unicorn.Element;
import ms.unicorn.language.languagedef.ElementDef;
import ms.unicorn.language.languagedef.ElementDefUtil;
import ms.unicorn.language.languagedef.EntityDef;
import ms.unicorn.language.languagedef.LanguageDef;
import ms.unicorn.language.languagedef.ModuleItem;
import ms.unicorn.language.languagedef.RelationDef;
import ms.unicorn.language.languagedef.TagDef;
import ms.unicorn.language.languagedef.TagDefUtil;
import ms.unicorn.language.languagedef.XbaseUtil;
import ms.unicorn.util.ReflectionUtil;

import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.emf.common.util.URI;
import org.eclipse.gmf.runtime.emf.type.core.ElementTypeRegistry;
import org.eclipse.gmf.runtime.emf.type.core.IElementType;
import org.eclipse.gmf.runtime.emf.type.core.IMetamodelType;
import org.eclipse.gmf.runtime.emf.type.core.ISpecializationType;
import org.eclipse.gmf.runtime.emf.type.core.requests.ConfigureRequest;
import org.eclipse.xtext.xbase.interpreter.IEvaluationContext;
import org.eclipse.xtext.xbase.interpreter.IExpressionInterpreter;
import org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter;

import com.google.inject.Inject;
import com.google.inject.Provider;

@SuppressWarnings("restriction")
public class ModelLanguage implements IModelLanguage {
	
	private ArrayList<LanguageElement> elements = new ArrayList<LanguageElement>();
	
	private Map<String, Object> caches = new HashMap<String, Object>();
	private IConfigurationElement configElement;
	private String pluginID;
	private LanguageDef def;
	
	@Inject 
	private Provider<IEvaluationContext> contextProvider;
	
	@Inject
	private XbaseInterpreter interpreter;
		
	public void initialize(IConfigurationElement item) {
		configElement = item;		
		IExtension extension = configElement.getDeclaringExtension();
		pluginID = extension.getNamespaceIdentifier();
		URI uri = URI.createPlatformPluginURI("/" + pluginID + "/" + configElement.getAttribute("definition"), true);
		def = LanguageRegistry.loadLanguageDefinition(uri);
		ReflectionUtil.registerBundle(pluginID);
		poplulateLanguageElements();
	}
	
	protected void poplulateLanguageElements() {
		XbaseUtil.interpreter = interpreter;
		XbaseUtil.context = contextProvider.get();
		
		for(ModuleItem eDef: def.getItems()) 
			if (eDef instanceof ElementDef) {
				if (eDef instanceof EntityDef)
					addElement(new LanguageEntity(this, (EntityDef) eDef));
				else
					addElement(new LanguageRelation(this, (RelationDef) eDef));
				TagDef fields = TagDefUtil.getTagDef(eDef, "fields");
				if (fields != null) 
					TagDefUtil.createEntityPhantomAttribute((ElementDef)eDef, fields);
			}
			
	}
		
	public void registerElementTypes() {
		for(LanguageElement e: elements) {
			IElementType type = e.getElementType();
			if (type instanceof IMetamodelType)
				ElementTypeRegistry.getInstance().register((IMetamodelType) type);
			else
				ElementTypeRegistry.getInstance().register((ISpecializationType)type);
		}
	}
	
	public IElementType getElementType(String typeId) {
		LanguageElement e = getElement(typeId);
		return e != null ? e.getElementType() : null;
	}
	
	@Override
	public Object getCachedObject(String typeName) {
		Object result = caches.get(typeName);
		return result;
	}
	
	@Override
	public void setCachedObject(Object object) {
		if (object != null)
			caches.put(object.getClass().getName(), object);
	}
	
	@Override
	public void configElement(Element element, ConfigureRequest request) {
		IElementType type = request.getTypeToConfigure();
		if (type instanceof HintedSpecializationType) {
			HintedSpecializationType hintType = (HintedSpecializationType) type;
			element.setElementType(type.getId());
			element.setLanguage(hintType.getLanguage().getId());
			LanguageElement langE = hintType.getElement();
			// create init values
			ElementDefUtil.initialize(langE, element);
			/*for(ElementAnnotation anno: langE.getAnnotations()) {
				element.getEAnnotations().add(anno.createEAnnotation());
			}*/
		}
	}
	
	@Override
	public int getElementCount() {
		return elements.size();
	}

	@Override
	public void addElement(LanguageElement element) {
		LanguageRegistry.INSTANCE.registerLanguageElement(element);
		elements.add(element);
	}

	@Override
	public LanguageElement getElement(int idx) {
		return elements.get(idx);
	}

	@Override
	public LanguageElement getElement(String id) {
		return LanguageRegistry.INSTANCE.getLanguageElement(id);
		/*for (LanguageElement e: elements)
			if (e.getId().equalsIgnoreCase(id))
				return e;
		return null;*/
	}

	@Override
	public void removeElement(String id) {
		for (int i = elements.size() - 1; i >= 0; i--)
			if (elements.get(i).getId().equalsIgnoreCase(id))
				elements.remove(i);
	}

	@Override
	public String getId() {
		return def.getName();
	}

	@Override
	public String getName() {
		return def.getText();
	}

	@Override
	public LanguageDef getDefinition() {
		return def;
	}

	@Override
	public void setDefinition(LanguageDef definition) {
		def = definition;
	}

	@Override
	public String getPluginID() {
		return this.pluginID;
	}

	public IEvaluationContext getContext() {
		if (contextProvider != null)
			return contextProvider.get();
		return null;
	}

	public IExpressionInterpreter getInterpreter() {
		if (interpreter == null)
			interpreter = new XbaseInterpreter();
		return interpreter;
	}

}
